module Pan
  
  # from squeak Color class :
  #
  #   | r g b max min |
  #   r _ self privateRed.
  #   g _ self privateGreen.
  #   b _ self privateBlue. 
  
  #   max _ min _ r.
  #   g > max ifTrue: [max _ g].
  #   b > max ifTrue: [max _ b].
  #   g < min ifTrue: [min _ g].
  #   b < min ifTrue: [min _ b].
  
  # max = 0
  # ifTrue: [ ^ 0.0 ]
  # ifFalse: [ ^ (max - min) asFloat / max asFloat ].
  
  class Color
    attr_accessor :r,:g,:b
    def initialize rgb = [255,255,255]
      if rgb.class == String 
        @r,@g,@b = parse_color rgb
      else
        @r,@g,@b = rgb
      end
    end
    def self.random
      Color.new([1,2,3].map{|ea| rand(256)})
    end
    def self.random_desaturated sat_level
      col = Color.random
      # does not really need to iterate! :)
      if col.saturation() > sat_level
        while col.saturation() > sat_level
          col = col.desaturate()
        end
      else
        while col.saturation() < sat_level
          col = col.saturate()
        end
      end
      col
    end
    
    def self.random_nice
      col = random_desaturated(0.3)
      while col.luminance() < 0.5
        col = random_desaturated(0.3)
      end
      col
    end
    
    def parse_color s_color
      color = s_color[0..-1]
      [0,2,4].map{|start| color[start..start+1]}.map{|ea| ea.hex}
    end
    def colors
      [@r,@g,@b]
    end
    def saturation
      min = colors.min
      max = colors.max
      if max == 0
        0.0
      else
        (max - min).to_f / max.to_f
      end
    end
    
    def desaturate step=1
      mid = ((@r+@g+@b).to_f/3.0).to_i
      r = move_towards @r, mid, step
      g = move_towards @g, mid, step
      b = move_towards @b, mid, step
      Color.new([r,g,b])
    end
    
    def saturate step=1
      mid = ((@r+@g+@b).to_f/3.0).to_i
      r = move_away @r, mid, step
      g = move_away @g, mid, step
      b = move_away @b, mid, step
      Color.new([r,g,b])
    end
    
    def luminance
      (@r/256.0 * 299 + 
       @g/256.0 * 587 + 
       @b/256.0 * 114)/1000.00
    end
    
    def to_html
      sprintf("#%02x%02x%02x",*colors)
    end
    
    def to_s
      sprintf("[%s, %s, %s]",*colors)
    end
    
    private
    
    def move_towards val, target, step
      if val < target
        nval = val + step
        nval = target if nval > target
      elsif val > target
        nval = val - step
        nval = target if nval < target
      else
        nval = val
      end
      nval
    end
    def move_away val, mid, step
      if val > mid
        nval = val + step
        nval = 255 if nval > 255
      elsif val < mid
        nval = val - step
        nval = 0 if nval < 0
      else
        nval = val
      end
      nval
    end
    def <=>(other)
      r,g,b = other.colors()
      return @r <=> r unless @r == r
      return @g <=> g unless @g == g
      return @b <=> b 
    end
  end
  
  
  if __FILE__ == $0
    require 'pp'
    puts "<table>"
    colors = (1..20).to_a.map{|ea| Color.random_nice()}
    puts "<tr>"
    puts "<th>Color</th>"
    puts "<th>Saturation</th>"
    puts "<th>Luminance</th>"
    colors.sort.each do |col|
      puts "<tr>"
      puts "<td bgcolor='#{col.to_html}'>#{col.to_s}</td>"
      puts "<td>#{col.saturation()}</td>"
      puts "<td>#{col.luminance()}</td>"
      puts "</tr>"
    end
    puts "</table>"
  end
  
end
